#include "playground.hpp"

#include <SDL.h>

#include <boost/lexical_cast.hpp>

#include <stdexcept>
#include <string>

namespace
{
	class lock
	{
	public:
		lock(SDL_Surface *_surface)
			: surface_(_surface)
		{
			if (!SDL_MUSTLOCK(surface_))
				return;

			if (SDL_LockSurface(surface_) == -1)
				throw std::runtime_error(
					"Couldn't initialize SDL library: "+std::string(SDL_GetError()));
		}

		~lock()
		{
			if (!SDL_MUSTLOCK(surface_))
				return;
			SDL_LockSurface(surface_);
		}
	private:
		SDL_Surface *surface_;
	};
}

cpptut::playground::playground(
	 unsigned width,
	 unsigned height,
	 unsigned zoom)
	 : running_(true),
	   width_(width * zoom),
	   height_(height * zoom),
	   zoom_(zoom),
	   surface_(0)
{
	if (SDL_Init(SDL_INIT_VIDEO) == -1)
		throw std::runtime_error(
		"Couldn't initialize SDL library: "+std::string(SDL_GetError()));

	surface_ = SDL_SetVideoMode(
		width_, 
		height_, 
		32, 
		SDL_SWSURFACE | SDL_ANYFORMAT);

	if (!surface_)
		throw std::runtime_error(
			"Couldn't initialize video surface: "+std::string(SDL_GetError()));

   SDL_WM_SetCaption("Playground", "Playground");
   SDL_Surface *logo = SDL_LoadBMP("..\\..\\..\\build\\Logo32.bmp");
   SDL_WM_SetIcon(logo, 0);
}

unsigned cpptut::playground::width() const
{
	return width_;
}

unsigned cpptut::playground::height() const
{
	return height_;
}

bool cpptut::playground::is_running() const
{
	return running_;
}

void cpptut::playground::set_pixel(
	unsigned x,
	unsigned y,
	unsigned r,
	unsigned g,
	unsigned b)
{
	for (unsigned px = x * zoom_; px < x * zoom_ + zoom_; ++px)
		for (unsigned py = y * zoom_; py < y * zoom_ + zoom_; ++py)
			set_pixel_lowlevel(px, py, r, g, b);
}

void cpptut::playground::update_screen()
{
	SDL_UpdateRect(
		surface_, 
		static_cast<Sint32>(0), 
		static_cast<Sint32>(0),
		static_cast<Sint32>(surface_->w),  
		static_cast<Sint32>(surface_->h));
}

void cpptut::playground::process_input()
{
	SDL_Event keyevent;

	while (SDL_PollEvent(&keyevent))
	{
		switch (keyevent.type)
		{
		case SDL_KEYDOWN:
			if (keyevent.key.keysym.sym == SDLK_ESCAPE)
				running_ = false;
			break;
		case SDL_QUIT:
			running_ = false;
			break;
		default:
			break;
		}
	}
}

void cpptut::playground::set_zoom(unsigned factor)
{
	zoom_ = factor;
}

unsigned cpptut::playground::zoom() const
{
	return zoom_;
}

int cpptut::playground::show()
{
	while (is_running())
	{
		update_screen();
		process_input();
	}
   return 0;
}

cpptut::playground::~playground()
{
	SDL_Quit();
}

void cpptut::playground::set_pixel_lowlevel(
	unsigned x,
	unsigned y,
	unsigned r,
	unsigned g,
	unsigned b)
{
	if (x >= width() || y >= height())
		throw std::runtime_error(
			"Invalid pixel coordinates: "+boost::lexical_cast<std::string>(x)+"x"+boost::lexical_cast<std::string>(y));

	Uint32 const color = SDL_MapRGBA(
		surface_->format,
		static_cast<Uint8>(r),
		static_cast<Uint8>(g),
		static_cast<Uint8>(b),
		static_cast<Uint8>(255));

	lock l(surface_);

	Uint32 * const p = 
		reinterpret_cast<Uint32 *>(
			static_cast<Uint8 *>(surface_->pixels) + y * surface_->pitch + x * surface_->format->BytesPerPixel);

	*p = color;
}
